﻿using System;
using System.Collections.Generic;
using System.Linq;
using Xenta.Api;
using Xenta.Entities;
using Xenta.Enums;
using Xenta.Security;
using Xenta.Structs;
using Xenta.Utils;

namespace Xenta.Operations
{
    #region __General__

    /// <summary>
    /// Creates a catalog supplier.
    /// </summary>
    public class CreateCatalogSupplier : CreateAuditable<CatalogSupplierEntity>,
        IApiOperation
    {
        #region Properties

        /// <summary>
        /// Gets the collection of security policies.
        /// </summary>
        public IEnumerable<SecurityPolicy> SecurityPolicies
        {
            get
            {
                yield return new SecurityPolicy()
                    .IsAuthenticated()
                    .HasPermission("CTLG");
                yield return new SecurityPolicy()
                    .IsAuthenticated()
                    .IsVerified()
                    .Rule((ctx, uow) =>
                    {
                        var manager = ctx.CatalogManager(uow);
                        return manager != null && 
                            manager.Flags.HasFlag(CatalogManagerFlags.Active) &&
                            manager.EntityID == ManagerID;
                    });
            }
        }

        /// <summary>
        /// Gets or sets the manager identifier.
        /// </summary>
        public int ManagerID
        {
            get;
            set;
        }

        /// <summary>
        /// Gets or sets the file group identifier.
        /// </summary>
        public int FileGroupID
        {
            get;
            set;
        }

        /// <summary>
        /// Gets or sets the flags.
        /// </summary>
        public CatalogSupplierFlags Flags
        {
            get;
            set;
        }

        #endregion

        #region Utilities

        protected override void Work(IUnitOfWork uow, out CatalogSupplierEntity result)
        {
            base.Work(uow, out result);
            if(ManagerID == 0) 
                return;

            uow.Execute(new MapCatalogManagerToCatalogSupplier
            {
                EntityID = result.EntityID,
                ChildID = ManagerID
            });
        }

        protected override void Construct(IUnitOfWork uow, CatalogSupplierEntity entity)
        {
            base.Construct(uow, entity);

            entity.FileGroup = FileGroupID != 0
                ? uow.Execute(new GetFileGroup
                {
                    ThrowsException = true,
                    EntityID = FileGroupID
                }).Result
                : null;
            entity.Flags = Flags;
        }

        #endregion
    }

    /// <summary>
    /// Updates the catalog supplier.
    /// </summary>
    public class UpdateCatalogSupplier : UpdateAuditable<CatalogSupplierEntity>,
        ISecuredOperation, IApiOperation
    {
        #region Properties

        /// <summary>
        /// Gets the collection of security policies.
        /// </summary>
        public IEnumerable<SecurityPolicy> SecurityPolicies
        {
            get
            {
                yield return new SecurityPolicy()
                    .IsAuthenticated()
                    .HasPermission("CTLG");
                yield return new SecurityPolicy()
                    .IsAuthenticated()
                    .IsVerified()
                    .Rule((ctx, uow) =>
                    {
                        var manager = ctx.CatalogManager(uow);
                        if(manager == null || !manager.Flags.HasFlag(CatalogManagerFlags.Active))
                            return false;
                        var supplier = uow.Execute(new GetCatalogSupplier
                        {
                            EntityID = EntityID
                        }).Result;
                        if(supplier == null)
                            return false;
                        return supplier.Managers.Contains(manager);
                    });
            }
        }

        /// <summary>
        /// Gets or sets the file group identifier.
        /// </summary>
        public int FileGroupID
        {
            get;
            set;
        }

        /// <summary>
        /// Gets or sets the flags.
        /// </summary>
        public CatalogSupplierFlags Flags
        {
            get;
            set;
        }

        #endregion

        #region Utilities

        protected override void Revise(IUnitOfWork uow, CatalogSupplierEntity entity)
        {
            base.Revise(uow, entity);

            entity.FileGroup = FileGroupID != 0 
                ? uow.Execute(new GetFileGroup
                {
                    ThrowsException = true,
                    EntityID = FileGroupID
                }).Result 
                : null;
            entity.Flags = Flags;
        }

        #endregion
    }

    /// <summary>
    /// Updates the catalog supplier statistics.
    /// </summary>
    public class UpdateCatalogSupplierStats : OperateEntityByID<CatalogSupplierEntity>
    {
        #region Utilities

        protected override void Work(IUnitOfWork uow, out CatalogSupplierEntity result)
        {
            var supplier = uow.Execute(new GetCatalogSupplier
            {
                ThrowsException = true,
                EntityID = EntityID
            }).Result;

            uow.Flush();
            uow.SqlQuery("exec fwk_CatalogSuppliers_UpdateStats @SupplierID=:SupplierID, @UpdatedOn=:UpdatedOn")
                .SetInt32("SupplierID", supplier.EntityID)
                .SetDateTime("UpdatedOn", DateTime.UtcNow)
                .ExecuteUpdate();
            uow.Flush();
            
            result = uow.Execute(new RefreshEntity<CatalogSupplierEntity>
            {
                Entity = supplier
            }).Result;
        }

        #endregion
    }

    /// <summary>
    /// Deletes the catalog supplier.
    /// </summary>
    public class DeleteCatalogSupplier : DeleteEntity<CatalogSupplierEntity>,
        ISecuredOperation, IApiOperation
    {
        #region Properties

        /// <summary>
        /// Gets the collection of security policies.
        /// </summary>
        public IEnumerable<SecurityPolicy> SecurityPolicies
        {
            get
            {
                yield return new SecurityPolicy()
                    .IsAuthenticated()
                    .HasPermission("CTLG");
                yield return new SecurityPolicy()
                    .IsAuthenticated()
                    .IsVerified()
                    .Rule((ctx, uow) =>
                    {
                        var manager = ctx.CatalogManager(uow);
                        if(manager == null || !manager.Flags.HasFlag(CatalogManagerFlags.Active))
                            return false;
                        var supplier = uow.Execute(new GetCatalogSupplier
                        {
                            EntityID = EntityID
                        }).Result;
                        if(supplier == null)
                            return false;
                        return supplier.Managers.Contains(manager);
                    });
            }
        }

        /// <summary>
        /// Gets or sets a value indicates, whether 
        /// delete the associated file group.
        /// </summary>
        public bool DeleteFileGroup
        {
            get;
            set;
        }

        #endregion

        #region Utilities

        protected override void Destruct(IUnitOfWork uow, CatalogSupplierEntity entity)
        {
            foreach(var i in entity.Translations)
            {
                uow.Execute(new DeleteCatalogSupplierTranslation
                {
                    EntityID = i.EntityID
                });
            }
            foreach(var i in entity.Ratings)
            {
                uow.Execute(new DeleteCatalogSupplierRating
                {
                    EntityID = i.EntityID
                });
            }
            foreach(var i in entity.Comments)
            {
                uow.Execute(new DeleteCatalogSupplierComment
                {
                    EntityID = i.EntityID
                });
            }
            foreach(var i in entity.Addresses)
            {
                uow.Execute(new DeleteCatalogSupplierAddress
                {
                    EntityID = i.EntityID
                });
            }
            foreach(var i in entity.Categories.ToList())
            {
                uow.Execute(new UnmapCatalogCategoryFromCatalogSupplier
                {
                    EntityID = entity.EntityID,
                    ChildID = i.EntityID
                });
            }
            foreach(var i in entity.Attributes.ToList())
            {
                uow.Execute(new UnmapLookupAttributeFromCatalogSupplier
                {
                    EntityID = entity.EntityID,
                    ChildID = i.EntityID
                });
            }
            foreach(var i in entity.Tags.ToList())
            {
                uow.Execute(new UnmapCatalogTagFromCatalogSupplier
                {
                    ChildID = i.EntityID,
                    EntityID = entity.EntityID
                });
            }
            foreach(var i in entity.Managers.ToList())
            {
                uow.Execute(new UnmapCatalogManagerFromCatalogSupplier
                {
                    ChildID = i.EntityID,
                    EntityID = entity.EntityID
                });
            }
            if(DeleteFileGroup && entity.FileGroup != null)
            {
                uow.Execute(new DeleteFileGroup
                {
                    EntityID = entity.FileGroup.EntityID,
                    DeleteFiles = true
                });
            }
            base.Destruct(uow, entity);
        }

        #endregion
    }

    /// <summary>
    /// Gets the catalog supplier by identifier.
    /// </summary>
    public class GetCatalogSupplier : GetEntity<CatalogSupplierEntity>,
        IApiOperation
    {
    }

    #endregion

    #region __Translation__

    /// <summary>
    /// Creates a supplier translation.
    /// </summary>
    public class CreateCatalogSupplierTranslation : CreateTranslation
        <TCatalogSupplierEntity, CatalogSupplierEntity>,
        ISecuredOperation, IApiOperation
    {
        #region Properties

        /// <summary>
        /// Gets the collection of security policies.
        /// </summary>
        public IEnumerable<SecurityPolicy> SecurityPolicies
        {
            get
            {
                yield return new SecurityPolicy()
                    .IsAuthenticated()
                    .HasPermission("CTLG");
                yield return new SecurityPolicy()
                    .IsAuthenticated()
                    .IsVerified()
                    .Rule((ctx, uow) =>
                    {
                        var manager = ctx.CatalogManager(uow);
                        if(manager == null || !manager.Flags.HasFlag(CatalogManagerFlags.Active))
                            return false;
                        var supplier = uow.Execute(new GetCatalogSupplier
                        {
                            EntityID = HolderID
                        }).Result;
                        if(supplier == null)
                            return false;
                        return supplier.Managers.Contains(manager);
                    });
            }
        }

        /// <summary>
        /// Gets or sets the name.
        /// </summary>
        public string Name
        {
            get;
            set;
        }

        /// <summary>
        /// Gets or sets the short description.
        /// </summary>
        public string ShortDescription
        {
            get;
            set;
        }

        /// <summary>
        /// Gets or sets the description.
        /// </summary>
        public string Description
        {
            get;
            set;
        }

        #endregion

        #region Utilities

        protected override void Construct(IUnitOfWork uow, TCatalogSupplierEntity entity)
        {
            base.Construct(uow, entity);

            entity.Name = StringHelper.EnsureNotNull(Name).TrimToLen(100);
            entity.ShortDescription = StringHelper.EnsureNotNull(ShortDescription).TrimToLen(1000);
            entity.Description = StringHelper.EnsureNotNull(Description);
        }

        #endregion
    }

    /// <summary>
    /// Updates the supplier translation.
    /// </summary>
    public class UpdateCatalogSupplierTranslation : UpdateTranslation
        <TCatalogSupplierEntity, CatalogSupplierEntity>,
        ISecuredOperation, IApiOperation
    {
        #region Properties

        /// <summary>
        /// Gets the collection of security policies.
        /// </summary>
        public IEnumerable<SecurityPolicy> SecurityPolicies
        {
            get
            {
                yield return new SecurityPolicy()
                    .IsAuthenticated()
                    .HasPermission("CTLG");
                yield return new SecurityPolicy()
                    .IsAuthenticated()
                    .IsVerified()
                    .Rule((ctx, uow) =>
                    {
                        var manager = ctx.CatalogManager(uow);
                        if(manager == null || !manager.Flags.HasFlag(CatalogManagerFlags.Active))
                            return false;
                        var translation = uow.Execute(new GetCatalogSupplierTranslation
                        {
                            EntityID = EntityID
                        }).Result;
                        if(translation == null)
                            return false;
                        return translation.Holder.Managers
                            .Contains(manager);
                    });
            }
        }

        /// <summary>
        /// Gets or sets the name.
        /// </summary>
        public string Name
        {
            get;
            set;
        }

        /// <summary>
        /// Gets or sets the short description.
        /// </summary>
        public string ShortDescription
        {
            get;
            set;
        }

        /// <summary>
        /// Gets or sets the description.
        /// </summary>
        public string Description
        {
            get;
            set;
        }

        #endregion

        #region Utilities

        protected override void Revise(IUnitOfWork uow, TCatalogSupplierEntity entity)
        {
            base.Revise(uow, entity);

            entity.Name = StringHelper.EnsureNotNull(Name).TrimToLen(100);
            entity.ShortDescription = StringHelper.EnsureNotNull(ShortDescription).TrimToLen(1000);
            entity.Description = StringHelper.EnsureNotNull(Description);
        }

        #endregion
    }

    /// <summary>
    /// Saves a supplier translation.
    /// </summary>
    public class SaveCatalogSupplierTranslation : SaveTranslation
        <TCatalogSupplierEntity, CatalogSupplierEntity>,
        ISecuredOperation, IApiOperation
    {
        #region Properties

        /// <summary>
        /// Gets the collection of security policies.
        /// </summary>
        public IEnumerable<SecurityPolicy> SecurityPolicies
        {
            get
            {
                yield return new SecurityPolicy()
                    .IsAuthenticated()
                    .HasPermission("CTLG");
                yield return new SecurityPolicy()
                    .IsAuthenticated()
                    .IsVerified()
                    .Rule((ctx, uow) =>
                    {
                        var manager = ctx.CatalogManager(uow);
                        if(manager == null || !manager.Flags.HasFlag(CatalogManagerFlags.Active))
                            return false;
                        var supplier = uow.Execute(new GetCatalogSupplier
                        {
                            EntityID = HolderID
                        }).Result;
                        if(supplier == null)
                            return false;
                        return supplier.Managers.Contains(manager);
                    });
            }
        }

        /// <summary>
        /// Gets or sets the name.
        /// </summary>
        public string Name
        {
            get;
            set;
        }

        /// <summary>
        /// Gets or sets the short description.
        /// </summary>
        public string ShortDescription
        {
            get;
            set;
        }

        /// <summary>
        /// Gets or sets the description.
        /// </summary>
        public string Description
        {
            get;
            set;
        }

        #endregion

        #region Utilities

        protected override void Work(IUnitOfWork uow, out TCatalogSupplierEntity result)
        {
            var translation = uow.Execute(new GetCatalogSupplierTranslationByLanguage
            {
                HolderID = HolderID,
                LanguageID = LanguageID
            }).Result;
            if(translation == null)
            {
                translation = uow.Execute(new CreateCatalogSupplierTranslation
                {
                    HolderID = HolderID,
                    LanguageID = LanguageID,
                    Name = Name,
                    ShortDescription = ShortDescription,
                    Description = Description
                }).Result;
            }
            else
            {
                translation = uow.Execute(new UpdateCatalogSupplierTranslation
                {
                    EntityID = translation.EntityID,
                    LanguageID = LanguageID,
                    Name = Name,
                    ShortDescription = ShortDescription,
                    Description = Description
                }).Result;
            }
            result = translation;
        }

        #endregion
    }

    /// <summary>
    /// Deletes the supplier translation.
    /// </summary>
    public class DeleteCatalogSupplierTranslation : DeleteTranlsation
        <TCatalogSupplierEntity, CatalogSupplierEntity>,
        ISecuredOperation, IApiOperation
    {
        #region Properties

        /// <summary>
        /// Gets the collection of security policies.
        /// </summary>
        public IEnumerable<SecurityPolicy> SecurityPolicies
        {
            get
            {
                yield return new SecurityPolicy()
                    .IsAuthenticated()
                    .HasPermission("CTLG");
                yield return new SecurityPolicy()
                    .IsAuthenticated()
                    .IsVerified()
                    .Rule((ctx, uow) =>
                    {
                        var manager = ctx.CatalogManager(uow);
                        if(manager == null || !manager.Flags.HasFlag(CatalogManagerFlags.Active))
                            return false;
                        var translation = uow.Execute(new GetCatalogSupplierTranslation
                        {
                            EntityID = EntityID
                        }).Result;
                        if(translation == null)
                            return false;
                        return translation.Holder.Managers
                            .Contains(manager);
                    });
            }
        }

        #endregion
    }

    /// <summary>
    /// Gets the supplier translation.
    /// </summary>
    public class GetCatalogSupplierTranslation : GetTranlsation
        <TCatalogSupplierEntity, CatalogSupplierEntity>,
        IApiOperation
    {
    }

    /// <summary>
    /// Gets the supplier translation.
    /// </summary>
    public class GetCatalogSupplierTranslationByLanguage : GetTranlsationByLanguage
        <TCatalogSupplierEntity, CatalogSupplierEntity>,
        IApiOperation
    {
    }

    #endregion

    #region __Search__

    /// <summary>
    /// Search over catalog supplier collection.
    /// </summary>
    public class SearchCatalogSuppliers : SearchOperation<CatalogSupplierEntity>,
        IApiOperation
    {
        #region Ctors

        /// <summary>
        /// Initializes a new class instance.
        /// </summary>
        public SearchCatalogSuppliers()
        {
            Term = SearchTerm.Empty;
            SortDir = SortDirection.Ascending;
            SortBy = new []
            {
                CatalogSupplierSortBy.ID
            };
        }

        #endregion

        #region Properties

        /// <summary>
        /// Gets or sets the search term.
        /// </summary>
        public SearchTerm Term
        {
            get;
            set;
        }

        /// <summary>
        /// Gets or sets the language identifier.
        /// </summary>
        public int? LanguageID
        {
            get;
            set;
        }

        /// <summary>
        /// Gets or sets the country identifier.
        /// </summary>
        public int? CountryID
        {
            get;
            set;
        }

        /// <summary>
        /// Gets or sets the region identifier.
        /// </summary>
        public int? RegionID
        {
            get;
            set;
        }

        /// <summary>
        /// Gets or sets the category identifier.
        /// </summary>
        public int? CategoryID
        {
            get;
            set;
        }

        /// <summary>
        /// Gets or sets the attribute identifiers.
        /// </summary>
        public int[] AttributeIDs
        {
            get;
            set;
        }

        /// <summary>
        /// Gets or sets the tag identifier.
        /// </summary>
        public int? TagID
        {
            get;
            set;
        }

        /// <summary>
        ///   Gets or sets the creation date 'from'.
        /// </summary>
        public DateTime? CreatedFrom
        {
            get;
            set;
        }

        /// <summary>
        ///   Gets or sets the creation date 'to'.
        /// </summary>
        public DateTime? CreatedTo
        {
            get;
            set;
        }

        /// <summary>
        /// Gets or sets the flag mask.
        /// </summary>
        public CatalogSupplierFlags FlagMask
        {
            get;
            set;
        }

        /// <summary>
        /// Gets or sets the sort direction.
        /// </summary>
        public SortDirection SortDir
        {
            get;
            set;
        }

        /// <summary>
        /// Gets or sets the sort by.
        /// </summary>
        public CatalogSupplierSortBy[] SortBy
        {
            get;
            set;
        }

        #endregion

        #region Utilities

        protected override IQueryable<CatalogSupplierEntity> Build(IUnitOfWork uow, IQueryable<CatalogSupplierEntity> query)
        {
            query = base.Build(uow, query);
            query = query.FilterByFlags(x => (int)x.Flags, (int)FlagMask);
            query = query.FilterByDateRange(x => x.CreatedOn, CreatedFrom, CreatedTo);
            if(LanguageID.HasValue)
                query = query.Where(x => x.Translations.Any(i => i.Language.EntityID == LanguageID.Value));
            switch(Term.Mode)
            {
                case SearchTerm.SearchMode.StartsWith:
                    if(LanguageID.HasValue)
                        query = query.Where(x => x.Translations.Any(i =>
                            i.Language.EntityID == LanguageID.Value &&
                            i.Name.StartsWith(Term.Keyword)));
                    else
                        query = query.Where(x => x.Translations.Any(i =>
                            i.Name.StartsWith(Term.Keyword)));
                    break;
                case SearchTerm.SearchMode.EndsWith:
                    if(LanguageID.HasValue)
                        query = query.Where(x => x.Translations.Any(i =>
                            i.Language.EntityID == LanguageID.Value &&
                            i.Name.EndsWith(Term.Keyword)));
                    else
                        query = query.Where(x => x.Translations.Any(i =>
                            i.Name.EndsWith(Term.Keyword)));
                    break;
                case SearchTerm.SearchMode.Contains:
                    if(LanguageID.HasValue)
                        query = query.Where(x => x.Translations.Any(i =>
                            i.Language.EntityID == LanguageID.Value &&
                            (i.Name.Contains(Term.Keyword) ||
                            i.ShortDescription.Contains(Term.Keyword))));
                    else
                        query = query.Where(x => x.Translations.Any(i =>
                            i.Name.Contains(Term.Keyword) ||
                            i.ShortDescription.Contains(Term.Keyword)));
                    break;
                case SearchTerm.SearchMode.Equals:
                    if(LanguageID.HasValue)
                        query = query.Where(x => x.Translations.Any(i =>
                            i.Language.EntityID == LanguageID.Value &&
                            i.Name.Equals(Term.Keyword)));
                    else
                        query = query.Where(x => x.Translations.Any(i =>
                            i.Name.Equals(Term.Keyword)));
                    break;
                default:
                    if(LanguageID.HasValue)
                        query = query.Where(x => x.Translations.Any(i => i.Language.EntityID == LanguageID.Value));
                    break;
            }
            if(CountryID.HasValue)
                query = query.Where(x => x.Addresses.Any(i => i.Country.EntityID == CountryID.Value));
            if(RegionID.HasValue)
                query = query.Where(x => x.Addresses.Any(i => i.Region.EntityID == RegionID.Value));
            if(CategoryID.HasValue)
            {
                query = CategoryID.Value != 0
                    ? query.Where(x => x.Categories.Any(i => i.EntityID == CategoryID.Value))
                    : query.Where(x => !x.Categories.Any());
            }
            if(TagID.HasValue)
                query = query.Where(x => x.Tags.Any(i => i.EntityID == TagID.Value));
            if(AttributeIDs != null && AttributeIDs.Any())
                query = query.Where(x => x.Attributes.Count(i => AttributeIDs.Contains(i.EntityID)) == AttributeIDs.Count());
            if(SortBy != null)
            {
                for(int i = 0; i < SortBy.Length; i++)
                {
                    switch(SortBy[i])
                    {
                        case CatalogSupplierSortBy.CommentCount:
                            query = query.OrderBy(x => x.CommentCount, SortDir, i != 0);
                            break;
                        case CatalogSupplierSortBy.RatingCount:
                            query = query.OrderBy(x => x.RatingCount, SortDir, i != 0);
                            break;
                        case CatalogSupplierSortBy.RatingAverage:
                            query = query.OrderBy(x => x.RatingAverage, SortDir, i != 0);
                            break;
                        case CatalogSupplierSortBy.ItemCount:
                            query = query.OrderBy(x => x.ItemCount, SortDir, i != 0);
                            break;
                        case CatalogSupplierSortBy.CreatedOn:
                            query = query.OrderBy(x => x.CreatedOn, SortDir, i != 0);
                            break;
                        default:
                            query = query.OrderBy(x => x.EntityID, SortDir, i != 0);
                            break;
                    }
                }
            }
            return query;
        }

        #endregion
    }

    /// <summary>
    /// Gets the collection of supplier as a dictionary, where 
    /// key - supplier identifier and value - supplier name.
    /// </summary>
    public class GetCatalogSupplierDictionary : SearchOperation<TCatalogSupplierEntity, Int32, String>,
        IApiOperation
    {
        #region Ctors

        /// <summary>
        /// Initializes a new class instance.
        /// </summary>
        public GetCatalogSupplierDictionary()
        {
            Term = SearchTerm.Empty;
        }

        #endregion

        #region Properties

        /// <summary>
        /// Gets or sets the language identifier.
        /// </summary>
        public int LanguageID
        {
            get;
            set;
        }

        /// <summary>
        /// Gets or sets the search term.
        /// </summary>
        public SearchTerm Term
        {
            get;
            set;
        }

        /// <summary>
        /// Gets or sets the flag mask.
        /// </summary>
        public CatalogSupplierFlags FlagMask
        {
            get;
            set;
        }

        #endregion

        #region Utilities

        protected override IQueryable<TCatalogSupplierEntity> Build(IUnitOfWork uow, IQueryable<TCatalogSupplierEntity> query)
        {
            query = base.Build(uow, query);
            query = query.FilterByFlags(x => (int)x.Holder.Flags, (int)FlagMask);
            query = query.Where(x => x.Language.EntityID == LanguageID);
            switch(Term.Mode)
            {
                case SearchTerm.SearchMode.StartsWith:
                    query = query.Where(x => x.Name.StartsWith(Term.Keyword));
                    break;
                case SearchTerm.SearchMode.EndsWith:
                    query = query.Where(x => x.Name.EndsWith(Term.Keyword));
                    break;
                case SearchTerm.SearchMode.Contains:
                    query = query.Where(x => x.Name.Contains(Term.Keyword));
                    break;
                case SearchTerm.SearchMode.Equals:
                    query = query.Where(x => x.Name.Equals(Term.Keyword));
                    break;
            }
            query = query.OrderBy(x => x.Name);
            return query;
        }

        protected override IQueryable<KeyValuePair<Int32, String>> Map(IQueryable<TCatalogSupplierEntity> query)
        {
            return query.Select(x => new KeyValuePair<Int32, String>(x.Holder.EntityID, x.Name));
        }

        #endregion
    }

    #endregion

    #region __Map__

    /// <summary>
    /// Maps the catalog category to catalog supplier.
    /// </summary>
    public class MapCatalogCategoryToCatalogSupplier : MapOperation
        <CatalogSupplierEntity, CatalogCategoryEntity>,
        ISecuredOperation, IApiOperation
    {
        #region Properties

        /// <summary>
        /// Gets the collection of security policies.
        /// </summary>
        public IEnumerable<SecurityPolicy> SecurityPolicies
        {
            get
            {
                yield return new SecurityPolicy()
                    .IsAuthenticated()
                    .HasPermission("CTLG");
                yield return new SecurityPolicy()
                    .IsAuthenticated()
                    .IsVerified()
                    .Rule((ctx, uow) =>
                    {
                        var manager = ctx.CatalogManager(uow);
                        if(manager == null || !manager.Flags.HasFlag(CatalogManagerFlags.Active))
                            return false;
                        var supplier = uow.Execute(new GetCatalogSupplier
                        {
                            EntityID = EntityID
                        }).Result;
                        if(supplier == null)
                            return false;
                        return supplier.Managers
                            .Contains(manager);
                    });
            }
        }

        #endregion

        #region Utilities

        protected override void Work(IUnitOfWork uow, out CatalogSupplierEntity result)
        {
            base.Work(uow, out result);
            uow.Execute(new UpdateCatalogCategoryStats
            {
                EntityID = ChildID
            });
        }

        #endregion
    }

    /// <summary>
    /// Unmaps the catalog category from catalog supplier.
    /// </summary>
    public class UnmapCatalogCategoryFromCatalogSupplier : UnmapOperation
        <CatalogSupplierEntity, CatalogCategoryEntity>,
        ISecuredOperation, IApiOperation
    {
        #region Properties

        /// <summary>
        /// Gets the collection of security policies.
        /// </summary>
        public IEnumerable<SecurityPolicy> SecurityPolicies
        {
            get
            {
                yield return new SecurityPolicy()
                    .IsAuthenticated()
                    .HasPermission("CTLG");
                yield return new SecurityPolicy()
                    .IsAuthenticated()
                    .IsVerified()
                    .Rule((ctx, uow) =>
                    {
                        var manager = ctx.CatalogManager(uow);
                        if(manager == null || !manager.Flags.HasFlag(CatalogManagerFlags.Active))
                            return false;
                        var supplier = uow.Execute(new GetCatalogSupplier
                        {
                            EntityID = EntityID
                        }).Result;
                        if(supplier == null)
                            return false;
                        return supplier.Managers
                            .Contains(manager);
                    });
            }
        }

        #endregion

        #region Utilities

        protected override void Work(IUnitOfWork uow, out CatalogSupplierEntity result)
        {
            base.Work(uow, out result);
            uow.Execute(new UpdateCatalogCategoryStats
            {
                EntityID = ChildID
            });
        }

        #endregion
    }

    /// <summary>
    /// Maps the lookup attribute to catalog supplier.
    /// </summary>
    public class MapLookupAttributeToCatalogSupplier : MapOperation
        <CatalogSupplierEntity, LookupAttributeEntity>,
        ISecuredOperation, IApiOperation
    {
        #region Properties

        /// <summary>
        /// Gets the collection of security policies.
        /// </summary>
        public IEnumerable<SecurityPolicy> SecurityPolicies
        {
            get
            {
                yield return new SecurityPolicy()
                    .IsAuthenticated()
                    .HasPermission("CTLG");
                yield return new SecurityPolicy()
                    .IsAuthenticated()
                    .IsVerified()
                    .Rule((ctx, uow) =>
                    {
                        var manager = ctx.CatalogManager(uow);
                        if(manager == null || !manager.Flags.HasFlag(CatalogManagerFlags.Active))
                            return false;
                        var supplier = uow.Execute(new GetCatalogSupplier
                        {
                            EntityID = EntityID
                        }).Result;
                        if(supplier == null)
                            return false;
                        return supplier.Managers
                            .Contains(manager);
                    });
            }
        }

        #endregion

        #region Utilities

        /// <summary>
        /// Performs the operation validation.
        /// </summary>
        /// <param name="uow">The unit of work.</param>
        /// <returns>true, whether the operation is allowed to be executed; otherwise false.</returns>
        protected override Err Validate(IUnitOfWork uow)
        {
            var attribute = uow.Execute(new GetLookupAttribute
            {
                EntityID = ChildID,
                ThrowsException = true
            }).Result;
            if(!attribute.Flags.HasFlag(LookupAttributeFlags.Common) &&
                !attribute.Flags.HasFlag(LookupAttributeFlags.CatalogSupplier))
            {
                return Err.Of(this).NotAllowed();
            }
            return base.Validate(uow);
        }

        #endregion
    }

    /// <summary>
    /// Unmaps the lookup attribute from catalog supplier.
    /// </summary>
    public class UnmapLookupAttributeFromCatalogSupplier : UnmapOperation
        <CatalogSupplierEntity, LookupAttributeEntity>,
        ISecuredOperation, IApiOperation
    {
        /// <summary>
        /// Gets the collection of security policies.
        /// </summary>
        public IEnumerable<SecurityPolicy> SecurityPolicies
        {
            get
            {
                yield return new SecurityPolicy()
                    .IsAuthenticated()
                    .HasPermission("CTLG");
                yield return new SecurityPolicy()
                    .IsAuthenticated()
                    .IsVerified()
                    .Rule((ctx, uow) =>
                    {
                        var manager = ctx.CatalogManager(uow);
                        if(manager == null || !manager.Flags.HasFlag(CatalogManagerFlags.Active))
                            return false;
                        var supplier = uow.Execute(new GetCatalogSupplier
                        {
                            EntityID = EntityID
                        }).Result;
                        if(supplier == null)
                            return false;
                        return supplier.Managers
                            .Contains(manager);
                    });
            }
        }
    }

    /// <summary>
    /// Maps the catalog manager to catalog supplier.
    /// </summary>
    public class MapCatalogManagerToCatalogSupplier : MapOperation
        <CatalogSupplierEntity, CatalogManagerEntity>,
        ISecuredOperation, IApiOperation
    {
        /// <summary>
        /// Gets the collection of security policies.
        /// </summary>
        public IEnumerable<SecurityPolicy> SecurityPolicies
        {
            get
            {
                yield return new SecurityPolicy()
                    .IsAuthenticated()
                    .HasPermission("CTLG");
                yield return new SecurityPolicy()
                    .IsAuthenticated()
                    .IsVerified()
                    .Rule((ctx, uow) =>
                    {
                        var manager = ctx.CatalogManager(uow);
                        if(manager == null ||
                            !manager.Flags.HasFlag(CatalogManagerFlags.Active))
                        {
                            return false;
                        }
                        var supplier = uow.Execute(new GetCatalogSupplier
                        {
                            EntityID = EntityID
                        }).Result;
                        if(supplier == null)
                            return false;
                        return supplier.Managers
                            .Contains(manager);
                    });
            }
        }
    }

    /// <summary>
    /// Unmaps the catalog manager from catalog supplier.
    /// </summary>
    public class UnmapCatalogManagerFromCatalogSupplier : UnmapOperation
        <CatalogSupplierEntity, CatalogManagerEntity>,
        ISecuredOperation, IApiOperation
    {
        /// <summary>
        /// Gets the collection of security policies.
        /// </summary>
        public IEnumerable<SecurityPolicy> SecurityPolicies
        {
            get
            {
                yield return new SecurityPolicy()
                    .IsAuthenticated()
                    .HasPermission("CTLG");
                yield return new SecurityPolicy()
                    .IsAuthenticated()
                    .IsVerified()
                    .Rule((ctx, uow) =>
                    {
                        var manager = ctx.CatalogManager(uow);
                        if(manager == null ||
                            !manager.Flags.HasFlag(CatalogManagerFlags.Active))
                        {
                            return false;
                        }
                        var supplier = uow.Execute(new GetCatalogSupplier
                        {
                            EntityID = EntityID
                        }).Result;
                        if(supplier == null)
                            return false;
                        return supplier.Managers
                            .Contains(manager);
                    });
            }
        }
    }

    /// <summary>
    /// Maps the catalog tag to catalog supplier.
    /// </summary>
    public class MapCatalogTagToCatalogSupplier : MapOperation
        <CatalogSupplierEntity, CatalogTagEntity>,
        ISecuredOperation, IApiOperation
    {
        #region Properties

        /// <summary>
        /// Gets the collection of security policies.
        /// </summary>
        public IEnumerable<SecurityPolicy> SecurityPolicies
        {
            get
            {
                yield return new SecurityPolicy()
                    .IsAuthenticated()
                    .HasPermission("CTLG");
                yield return new SecurityPolicy()
                    .IsAuthenticated()
                    .IsVerified()
                    .Rule((ctx, uow) =>
                    {
                        var manager = ctx.CatalogManager(uow);
                        if(manager == null || !manager.Flags.HasFlag(CatalogManagerFlags.Active))
                            return false;
                        var supplier = uow.Execute(new GetCatalogSupplier
                        {
                            EntityID = EntityID
                        }).Result;
                        if(supplier == null)
                            return false;
                        return supplier.Managers
                            .Contains(manager);
                    });
            }
        }

        #endregion

        #region Utilities

        protected override void Work(IUnitOfWork uow, out CatalogSupplierEntity result)
        {
            base.Work(uow, out result);
            uow.Execute(new UpdateCatalogTagStats
            {
                EntityID = ChildID
            });
        }

        #endregion
    }

    /// <summary>
    /// Unmaps the catalog tag from catalog supplier.
    /// </summary>
    public class UnmapCatalogTagFromCatalogSupplier : UnmapOperation
        <CatalogSupplierEntity, CatalogTagEntity>,
        ISecuredOperation, IApiOperation
    {
        #region Properties

        /// <summary>
        /// Gets the collection of security policies.
        /// </summary>
        public IEnumerable<SecurityPolicy> SecurityPolicies
        {
            get
            {
                yield return new SecurityPolicy()
                    .IsAuthenticated()
                    .HasPermission("CTLG");
                yield return new SecurityPolicy()
                    .IsAuthenticated()
                    .IsVerified()
                    .Rule((ctx, uow) =>
                    {
                        var manager = ctx.CatalogManager(uow);
                        if(manager == null || !manager.Flags.HasFlag(CatalogManagerFlags.Active))
                            return false;
                        var supplier = uow.Execute(new GetCatalogSupplier
                        {
                            EntityID = EntityID
                        }).Result;
                        if(supplier == null)
                            return false;
                        return supplier.Managers
                            .Contains(manager);
                    });
            }
        }

        #endregion

        #region Utilities

        protected override void Work(IUnitOfWork uow, out CatalogSupplierEntity result)
        {
            base.Work(uow, out result);
            uow.Execute(new UpdateCatalogTagStats
            {
                EntityID = ChildID
            });
        }

        #endregion
    }

    #endregion
}
